/*
 * Copyright 2010 Yannick Stucki (yannickstucki.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.yannickstucki.android.musicqueue;

import java.util.ArrayList;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

import com.yannickstucki.android.musicqueue.communication.ISongCommander;
import com.yannickstucki.android.musicqueue.communication.ISongEnqueuer;
import com.yannickstucki.android.musicqueue.communication.OnSongsUpdatedListener;
import com.yannickstucki.android.musicqueue.communication.OnUiUpdateListener;
import com.yannickstucki.android.musicqueue.data.DatabaseFacade;
import com.yannickstucki.android.musicqueue.data.IDatabase;
import com.yannickstucki.android.musicqueue.data.DbAdapter;
import com.yannickstucki.android.musicqueue.data.Song;
import com.yannickstucki.android.musicqueue.data.SongCursorAll;
import com.yannickstucki.android.musicqueue.data.SongCursorStarred;
import com.yannickstucki.android.musicqueue.data.SongLibrary;
import com.yannickstucki.android.musicqueue.data.SongUpdate;
import com.yannickstucki.android.musicqueue.data.SongUpdateResult;
import com.yannickstucki.android.musicqueue.data.SongUpdateTask;
import com.yannickstucki.android.musicqueue.playback.MusicPlayer;
import com.yannickstucki.android.musicqueue.playback.MusicService;
import com.yannickstucki.android.musicqueue.playback.Notificator;
import com.yannickstucki.android.musicqueue.songpicking.RandomSongPicker;
import com.yannickstucki.android.musicqueue.ui.Screen;
import com.yannickstucki.android.musicqueue.util.Logger;

/**
 * The central controller of the MVC pattern. It is a Singleton class. It
 * controls the media playback service, and notifies the UI when it has to
 * change.
 * 
 * This class is ugly and does all the constructor work, therefore it is also
 * not testable. It contains as little logic as possible.
 * 
 * @author Yannick Stucki (yannickstucki@gmail.com)
 * 
 */
public class SongController implements OnSongsUpdatedListener {

  /**
   * Singleton class.
   */
  private static SongController instance = new SongController();

  /**
   * Contains all the songs.
   */
  private SongLibrary songLibrary;

  /**
   * Service that plays the music.
   */
  private MusicService musicService;

  private OnSongsUpdatedListener onSongsUpdatedListener;

  /**
   * Flag so this class only gets initialized once.
   */
  private boolean initialized = false;

  private Context context;

  private IDatabase database;

  /**
   * Singleton class has private constructor.
   */
  private SongController() {
  }

  /**
   * Returns the single instance.
   */
  public static SongController getInstance() {
    return instance;
  }

  public boolean isInitialized() {
    return initialized;
  }

  /**
   * Initializes the object. This includes starting the music service and
   * filling the library with songs.
   */
  public boolean initialize(Context context, OnSongsUpdatedListener onSongsUpdatedListener) {
    Logger.out("initializing controller");
    this.context = context;
    this.onSongsUpdatedListener = onSongsUpdatedListener;

    Intent serviceIntent = new Intent(context, MusicService.class);
    context.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
    database = new DatabaseFacade(new DbAdapter(context), context.getContentResolver()).open();
    SongUpdate songUpdate = new SongUpdate(new SongCursorAll(database), new SongCursorStarred(
        database), this);

    SongUpdateTask task = new SongUpdateTask();
    task.execute(songUpdate);

    initialized = true;
    return true;
  }

  /**
   * Returns the number of songs displayed on the main screen.
   * 
   * TODO: This will depend on screen sizes.
   */
  public int getDisplayedSongCount() {
    return 5;
  }

  public void setOnUiUpdateListener(OnUiUpdateListener listener) {
    songLibrary.setOnUiUpdateListener(listener);
  }

  /**
   * Called after the songs in the SongLibrary have been asynchronously updated.
   */
  @Override
  public void onSongsUpdated(SongUpdateResult result) {
    Logger.out("songs are updated");

    NotificationManager notificationManager = (NotificationManager) context
        .getSystemService(Context.NOTIFICATION_SERVICE);
    PendingIntent intent = PendingIntent.getActivity(context, 0, new Intent(context, Screen.class),
        0);
    MusicPlayer musicPlayer
      = new MusicPlayer(new Notificator(context, notificationManager, intent));

    RandomSongPicker songPicker = new RandomSongPicker();
    songLibrary = new SongLibrary(musicPlayer, songPicker, database, result.getAllSongs(),
        result.getStarredSongs());
    songPicker.setAllSongs(songLibrary.getAllSongs());

    context = null;
    database = null;
    if (!songLibrary.getAllSongs().isEmpty()) {
      songLibrary.newDisplayedSongs(getDisplayedSongCount());
      songLibrary.play(); // TODO doens't work because of async
      musicPlayer.setOnSongCompletedListener(songLibrary);
    } else {
      Logger.debug("No songs");
    }

    // TODO temporary
    onSongsUpdatedListener.onSongsUpdated(null);
    onSongsUpdatedListener = null;
  }

  /**
   * Connection to the music service.
   */
  private ServiceConnection connection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
      Logger.out("Service connected");
      musicService = ((MusicService.LocalBinder) service).getService();
    }

    public void onServiceDisconnected(ComponentName className) {
      Logger.out("Service disconnected");
      musicService = null;
    }
  };

  /**
   * Called when the activity got destroyed. If no music is playing, we can then
   * also destroy the service to free the resources.
   * 
   * TODO: setting initialized to false also causes a rescan of the library...
   * unnecessary.
   */
  public void onActivityDestroyed() {
    // if (!musicPlayer.isPlaying()) {
    // musicService.stopSelf();
    // initialized = false;
    // }
  }

  public void onServiceDestroyed() {

  }

  public ISongCommander getSongCommander() {
    return songLibrary;
  }

  public ISongEnqueuer getSongEnqueuer() {
    return songLibrary;
  }

  public ArrayList<Song> getAllSongs() {
    return songLibrary.getAllSongs();
  }

  public ArrayList<Song> getStarredSongs() {
    return new ArrayList<Song>(songLibrary.getStarredSongs());
  }
}
